package com.arsn.myf.common.aspect;

import com.alibaba.fastjson.JSON;
import com.arsn.myf.common.annotation.ApiLog;
import com.arsn.myf.common.event.ApiLogEvent;
import com.arsn.myf.common.event.ErrorLogEvent;
import com.arsn.myf.common.util.auth.AuthUtil;
import com.arsn.myf.common.util.spring.SpringUtils;
import com.arsn.myf.security.entity.UserInfo;
import com.arsn.myf.system.entity.ErrorLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * log 切面
 * @Around 通知方法将目标方法封装起来
 * @Before 通知方法会在目标方法调用之前执行
 * @After 通知方法会在目标方法返回或者异常后执行
 * @AfterReturning 通知方法会在目标方法返回时执行
 * @Afterthrowing 通知方法会在目标方法抛出异常时执行
 * @author zhongrj
 * @since 2022-0809
 */
@Aspect
@Component
public class LogAspect {

    @Autowired
    private ApplicationContext applicationContext;


    /**
     * 设置日志操作切入点，记录操作日志
     */
    @Pointcut("@annotation(com.arsn.myf.common.annotation.ApiLog)")
    public void logPointCut(){}

    /**
     * 统一切入点 包 com.arsn.myf..controller.. 下包中所有类的所有方法切面（主要获取所有的异常）
     */
    @Pointcut("execution(public * com.arsn.myf..controller..*.*(..))")
    public void pointCut(){
    }

    /**
     * 前置通知（切点）
     * @param joinPoint
     */
    @Before("logPointCut()")
    public void beforeMethod(JoinPoint joinPoint){
    }

    /**
     * 后置通知（切点）
     * @param joinPoint
     */
    @After("logPointCut()")
    public void afterMethod(JoinPoint joinPoint){
    }

    /**
     * 处理完请求执行
     * @param joinPoint
     * @param result
     */
    @AfterReturning(value = "logPointCut()",returning = "result")
    public void afterReturningMethod(JoinPoint joinPoint,Object result){
        //log 数据处理
        logDataHandler(joinPoint,null,result);
    }

    /**
     * 出现异常时后执行
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value = "pointCut()",throwing = "e")
    public void afterThrowingMethod(JoinPoint joinPoint,Exception e){
        //log 数据处理
        logDataHandler(joinPoint,e,null);
    }

//    /**
//     * 环绕切点
//     * @param proceedingJoinPoint
//     */
//    @Around(value = "logPointCut()")
//    public void aroundMethod(ProceedingJoinPoint proceedingJoinPoint){
//
//    }


    /**
     * log 数据处理
     * @param joinPoint 切点对象
     * @param exception 异常对象
     * @param result 响应结果
     */
//    @Async
    public void logDataHandler(JoinPoint joinPoint,Exception exception,Object result){
        Map<String, Object> map = new HashMap<>(1);

        //获取request 对象
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        //获取请求地址
        String remoteAddr = request.getRemoteAddr();
        //获取 uri
        String requestURI = request.getRequestURI();
        //获取请求方式
        String methodType = request.getMethod();

        Object targetObj = joinPoint.getTarget();
        //获取类名
        String className = targetObj.getClass().getName();
        //获取方法名
        String methodName = joinPoint.getSignature().getName();
        String username = "";
        //操作人
//        Object principal = SecurityUtils.getLoginUser();
        UserInfo user = AuthUtil.getUser();
        if (null!=user) {
            if (!user.getUsername().equals("anonymousUser")) {
                username = user.getUsername();
            }
        }
        // 参数名
        String[] argNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames();
        //获取请求参数值
        Object[] args = joinPoint.getArgs();
        StringBuilder stringBuilder = new StringBuilder();
        if (args.length>0) {
            for (String argName : argNames) {
                stringBuilder.append(argName + "=%s&");
            }
            if (requestURI.contains("login")){
                username = args[0].toString();
            }
        }
        String paramName = "";
        String paramFormat = "";
        if (stringBuilder.toString().length()>0) {
            //toString,去除最后的 &
            paramName = stringBuilder.toString().substring(0, stringBuilder.toString().length() - 1);
            //格式化
            paramFormat = String.format(paramName, args);
        }

        //获取服务器名称
        InetAddress host = null;
        String hostName = null;
        String hostAddress = null;
        try {
            host = InetAddress.getLocalHost();
            //服务器名
            hostName = host.getHostName();
            //获取服务器 ip
            hostAddress = host.getHostAddress();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }

        //获取运行环境
        String profile = SpringUtils.getActiveProfile();

        //判断是否为异常消息
        if (null==exception){
            com.arsn.myf.system.entity.ApiLog apiLog = new com.arsn.myf.system.entity.ApiLog();
            //获取注解
            MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
            Method method = methodSignature.getMethod();
            String title = "";
            if (null!=method){
                ApiLog apiLogs = method.getAnnotation(ApiLog.class);
                title = apiLogs.value();
            }
            //返回结果
            String jsonString = JSON.toJSONString(result);
            //数据封装
            apiLog.setTitle(title);
            apiLog.setEnv(profile);
            apiLog.setRequestParam(paramFormat);
            apiLog.setJsonResult(jsonString);
            apiLog.setMethod(methodType);
            apiLog.setMethodName(methodName);
            apiLog.setMethodClass(className);
            apiLog.setRemoteIp(remoteAddr);
            apiLog.setRequestUri(requestURI);
            apiLog.setRemoteIp(remoteAddr);
            apiLog.setServerHost(hostName);
            apiLog.setServerIp(hostAddress);
            apiLog.setStatus(1);
            apiLog.setCreateBy(username);

            map.put("log",apiLog);
            //发布事件
            applicationContext.publishEvent(new ApiLogEvent(map));
        }else {
            //堆栈信息处理，取出当前系统包下的异常信息
            StackTraceElement[] stackTrace = exception.getStackTrace();
            StringBuilder builder = new StringBuilder("\n").append(exception);
            for (StackTraceElement traceElement : stackTrace) {
                if (!traceElement.getClassName().startsWith("com.arsn.myf")){
                    break;
                }
                builder.append("\n\tat ").append(traceElement);
            }

            //数据封装
            ErrorLog errorLog = new ErrorLog();
            errorLog.setEnv(profile);
            errorLog.setRequestParam(paramFormat);
            errorLog.setMessage(exception.getMessage());
            errorLog.setExceptionName(exception.getClass().getName());
            errorLog.setStackTrace(builder.toString());
            errorLog.setLineNumber(exception.getStackTrace()[0].getLineNumber());
            errorLog.setExceptionName(exception.getClass().getName());
            errorLog.setMethod(methodType);
            errorLog.setMethodName(methodName);
            errorLog.setMethodClass(className);
            errorLog.setRemoteIp(remoteAddr);
            errorLog.setRequestUri(requestURI);
            errorLog.setRemoteIp(remoteAddr);
            errorLog.setServerHost(hostName);
            errorLog.setServerIp(hostAddress);
            errorLog.setCreateBy(username);

            map.put("log",errorLog);
            //发布事件
            applicationContext.publishEvent(new ErrorLogEvent(map));
        }
    }

}
